package scanner

import (
	"io/ioutil"
	"learning/gooop/spring/autogen/common"
	"learning/gooop/spring/autogen/domain"
	"regexp"
	"strings"
	"unicode"
)

type IFileScanner interface {
	ScanCodeFile(pkg *domain.PackageInfo, file string) (error, *domain.CodeFileInfo)
}

type tFileScanner int

func (me *tFileScanner) ScanCodeFile(pkg *domain.PackageInfo, file string) (error, *domain.CodeFileInfo) {
	fbytes, e := ioutil.ReadFile(file)
	if e != nil {
		return e, nil
	}

	ftext := string(fbytes)
	lines := strings.Split(ftext, "\n")
	for i, it := range lines {
		lines[i] = strings.TrimRightFunc(it, unicode.IsSpace)
	}

	codeFile := domain.NewCodeFileInfo()
	codeFile.Package = pkg
	codeFile.RawLines = lines

	// clean comments
	me.fileCleanComments(codeFile)

	// parse imports
	DefaultImportScanner.ScanImport(codeFile)

	// todo: parse struct declares/fields/methods

	return nil, nil
}

func (me *tFileScanner) fileCleanComments(codeFile *domain.CodeFileInfo) {
	bInParaComment := false
	cleanLines := make([]string, len(codeFile.RawLines))
	for i, it := range codeFile.RawLines {
		s := it

		if bInParaComment {
			// para comment end?
			i := strings.Index(it, gParaCommentEnd)
			if i >= 0 {
				bInParaComment = false
				s = s[i+1:]

			} else {
				cleanLines[i] = ""
				continue
			}
		}

		if common.Tokens.MatchString(it, gLineCommentPrefix) {
			cleanLines[i] = ""
			continue
		}

		s = me.fileRemoveParaCommentInLine(it)
		i1 := strings.Index(s, gParaCommentStart)
		if i1 >= 0 {
			s = s[:i1]
			bInParaComment = true
		}
		cleanLines[i] = s
	}
}

func (me *tFileScanner) fileRemoveParaCommentInLine(s string) string {
	arr := gParaCommentInLine.FindAllStringIndex(s, -1)
	if len(arr) > 0 {
		for i := len(arr) - 1; i >= 0; i-- {
			from := arr[i][0]
			to := arr[i][1]
			s = s[:from] + s[to+1:]
		}
	}

	return s
}

var gLineCommentPrefix = "^\\s*//"
var gParaCommentInLine = regexp.MustCompile("/\\*.*\\*/")
var gParaCommentStart = "/*"
var gParaCommentEnd = "*/"

var DefaultFileScanner = new(tFileScanner)
